#include "netstatext.h"

#include <QApplication>
#include <QFileInfo>
#include <QtDeclarative>


NetStatProc::NetStatProc(QObject *_parent) : QThread(_parent)
{
    netstat = new NetStat( this );
    flPacket = "tcp";
    flResolve = false;
    flDesktopFilesInfo = false;
}

NetStatProc::~NetStatProc()
{
    if( netstat != NULL ) {
        delete netstat;
    }
}

void NetStatProc::setParam(const bool _flResolve)
{
    this->flResolve = _flResolve;
}

void NetStatProc::run()
{
    if( flPacket == "tcp" ) {
        listOut = this->netstat->getStatTCP( flResolve );
    } else if( flPacket == "udp" ) {
        listOut = this->netstat->getStatUDP( flResolve );
    } else {
        return;
    }

    if( flDesktopFilesInfo ) {
        this->getInfoDesktopFiles();
        /* при удачном србатывании, повторно запускать ф-цию нет необходимости */
        flDesktopFilesInfo = false;
    }
}

void NetStatProc::getInfoDesktopFiles()
{
    QDir p("/usr/share/applications");

    QStringList listfiles = p.entryList( QDir::Files );

    QRegExp patIcon("(^Icon(.)*=)");
    QRegExp patExec("(^Exec(.)*=)");
    QRegExp patName("(^Name(.)*=)");

    /* просмотр всех desktop файлов */
    for( int k=0; k<listfiles.length(); k++ )
    {
        /* открываем desktop файл и читаем по строке, чтобы найти необходимые строки */
        QString fileDesktopPath = p.path() + QString("/") + listfiles[ k ];
        QFile fileDesktop( fileDesktopPath );
        if (!fileDesktop.open(QIODevice::ReadOnly | QIODevice::Text)) {
            continue;
        }

        QMap<QString, QString> *bufDesktopFiles = new QMap<QString, QString>();

        QTextStream in(&fileDesktop);
        while ( !in.atEnd() ) {
            QString line = in.readLine();

            if( patIcon.indexIn( line ) != -1 ) {
                bufDesktopFiles->insert("icon", line.trimmed() );
            } else if( patExec.indexIn( line ) != -1 ) {
                bufDesktopFiles->insert("exec", line.trimmed() );
            } else if( patName.indexIn( line ) != -1 ) {
                bufDesktopFiles->insert("name", line.trimmed() );
            }
        } //while

        /* если искомые строки найдены, сохраним их */
        if( (bufDesktopFiles->contains("icon")) && (bufDesktopFiles->contains("exec")) ) {
            __listDesktopFiles.append( bufDesktopFiles );
        } else {
            delete bufDesktopFiles;
        }

    } // for
    //qDebug() << "NetStatProc::getInfoDesktopFiles(): __listDesktopFiles.length() = " << __listDesktopFiles.length();
}

/************************************************************************************************************/
NetStatExt::NetStatExt(QObject *_parent) : QObject(_parent)
{
    parent = _parent;

    flPacket = "tcp";

    listModel = NULL;

    procNetStat = NULL;
    procNetStat = new NetStatProc( this );
    QObject::connect( procNetStat, SIGNAL(started()), SLOT(startNetstat()) );
    QObject::connect( procNetStat, SIGNAL(finished()), SLOT(finishNetstat()) );
}

NetStatExt::~NetStatExt()
{
    if( procNetStat != NULL ) {
        delete procNetStat;
    }
    clearListResult();
    clearListDesktop();
}

/* получение данных из /proc (запуск отдельной нити) */
void NetStatExt::getData()
{
    procNetStat->workNetStat();

    if( flPacket == "tcp" ) {
        procNetStat->needTCP();
    } else if( flPacket == "udp" ) {
        procNetStat->needUDP();
    } else {
        return;
    }

    procNetStat->setParam( flResolve );

    if( listDesktopFiles.length() == 0 ) {
        clearListDesktop();
        procNetStat->desktopFilesInfo( true );
    } else {
        procNetStat->desktopFilesInfo( false );
    }

    procNetStat->start();
}

void NetStatExt::startNetstat()
{
    //qDebug() << "NetStatExt::startNetstat(): the thread start !";
    this->clearListResult();
    QDeclarativeView *qmlObj = (QDeclarativeView*)(this->parent);
    pageNetworkConnections = (qmlObj->rootObject())->findChild<QObject*>( QString("pageNetworkConnections") );
    if( pageNetworkConnections != NULL ) {
        //qDebug() << " NetStatExt::startNetstat(): pageNetworkConnections found";
        QMetaObject::invokeMethod(pageNetworkConnections, "showBanner");
        QMetaObject::invokeMethod(pageNetworkConnections, "disabledWhenUpdate");
    }
}

/* ф-ция запускается, когда заканчивает работу нить получающая информ о коннектах */
void NetStatExt::finishNetstat()
{
    data = procNetStat->getResult();

    if( listDesktopFiles.length() == 0 ) {
        listDesktopFiles = procNetStat->getResultDesktopFiles();
    }

    if( listModel == NULL ) {
        listModel = new NetStatListModel( this );
    }

    QList<QMap<QString, QVariant>*>::const_iterator itr;
    for( itr = data.constBegin(); itr != data.constEnd(); itr++ )
    {
        QMap<QString, QVariant>* pQMapData = *(itr);

        QString __stat = (pQMapData->value("state")).toString(); // QVarian ==> QString
        QString __cmd = (pQMapData->value("cmd")).toString();
        QString __faddr = (pQMapData->value("foreign_addr")).toString();
        QString __fport = (pQMapData->value("foreign_port")).toString();

        /* согласно состоянию соединения, получим картунку с шестеренкой разного цвета и описание */
        NetStateH nsh = this->netSateHuman( __stat );

        QString proc_pic("pictures/conn.png");
        QString proc_name("");

        /* ищем приложение среди .desktop файлов */
        ParamAppN950 pan9;
        pan9.icon = "";
        pan9.name = "";
        if( !( __cmd.isEmpty() || __cmd.isNull()) ) {
            pan9 = this->findAppN950( __cmd );
        }
        if( pan9.icon != "" ) {
            /* приложение найдено, используем полученную картунку */
            proc_pic = pan9.icon;
            proc_name = pan9.name;
        } else {
            /* приложение не найдено, поиск среди предопределенных */
            ParamAppN950 pdef_pan9 = this->predefineAppN9( __cmd );
            if( pdef_pan9.name != "" ) {
                proc_pic = pdef_pan9.icon;
                proc_name = pdef_pan9.name;
            } else {
                /* приложение не найдено, присваеваем картинку с шестеренкой */
                proc_pic = nsh.pic;
            }
        }

        if( (__stat == "LISTEN") || ( (__faddr == "0.0.0.0") && (__fport == "0" )) ) {
            __faddr = "";
            __fport = "";
        }

        QString __pid = (pQMapData->value("pid")).toString();
        QString __laddr = (pQMapData->value("local_addr")).toString();
        QString __lport = (pQMapData->value("local_port")).toString();

        if( ( __pid.isEmpty() || __pid.isNull()) ) {
            __pid = "";
        }

        /* текстовое описание сетевого состояния */
        QString longDescrNetState = this->descrNetState( __stat );

        listModel->append( __stat, proc_pic, nsh.txt, longDescrNetState, __cmd, proc_name, __pid, __laddr, __lport, __faddr, __fport );
        //qDebug()<<__stat<<" "<<__laddr<<":"<<__lport<<" => "<<__faddr<<":"<<__fport<<" pid:"<<__pid<<" pic:"<<proc_pic<<__cmd<<proc_name;

    } // for( itr = data.constBegin(); itr != data.constEnd(); itr++ )

    //qDebug() << "NetStatExt::finishNetstat(): listModel.count()=" << listModel->count();

    QDeclarativeView *qmlObj = (QDeclarativeView*)(this->parent);
    QDeclarativeEngine *engine = qmlObj->engine();
    engine->rootContext()->setContextProperty( "cppNetStatListModel", listModel );

    if( pageNetworkConnections != NULL ) {
        QMetaObject::invokeMethod(pageNetworkConnections, "hideBanner");
        QMetaObject::invokeMethod(pageNetworkConnections, "enabledAfterUpdate");
    } else {
        QDeclarativeView *qmlObj = (QDeclarativeView*)(this->parent);
        pageNetworkConnections = (qmlObj->rootObject())->findChild<QObject*>( QString("pageNetworkConnections") );
        if( pageNetworkConnections != NULL ) {
            QMetaObject::invokeMethod(pageNetworkConnections, "hideBanner");
            QMetaObject::invokeMethod(pageNetworkConnections, "enabledAfterUpdate");
        }
    }
}


NetStateH  NetStatExt::netSateHuman( const QString &_state ) const
{
    NetStateH retVal;
    retVal.pic = "pictures/conn.png";
    retVal.txt = "";
    if( _state == "LISTEN" ) {
        retVal.txt = qApp->translate( "NetStatExt", "Waiting for connection on:" );
        retVal.pic = "pictures/conn-L.png";
    } else if( _state == "SYN_SENT" ) {
        retVal.txt = qApp->translate( "NetStatExt", "Attempt to connect to:" );
        retVal.pic = "pictures/conn-TC.png";
    } else if( _state == "SYN_RECV" ) {
        retVal.txt = qApp->translate( "NetStatExt", "Accepting the connection from:" );
        retVal.pic = "pictures/conn-TC.png";
    } else if( _state == "ESTABLISHED" ) {
        retVal.txt = qApp->translate( "NetStatExt", "Connection is established with:" );
        retVal.pic = "pictures/conn-E.png";
    } else if( (_state == "CLOSE_WAIT") || (_state == "LAST_ACK") || (_state == "FIN_WAIT1") || (_state == "FIN_WAIT2") || (_state == "CLOSING") || (_state == "TIME_WAIT") ) {
        retVal.txt = qApp->translate( "NetStatExt", "Closing the connection with:" );
        retVal.pic = "pictures/conn-CC.png";
    } else if( _state == "CLOSE" ) {
        retVal.txt = qApp->translate( "NetStatExt", "Connection is closed" );
        retVal.pic = "pictures/conn-CC.png";
    }

    return retVal;
}

QString NetStatExt::descrNetState( const QString &state) const
{
    QString txt = "";
    if( state == "LISTEN" ) {
        //txt = qApp->tr("NetStatExt", "Device (normally a server) is waiting for client connection.", NULL, QApplication::UnicodeUTF8 );
        txt = qApp->translate("NetStatExt", "Device (normally a server) is waiting for client connection.");
    } else if( state == "SYN_SENT" ) {
        txt = qApp->translate("NetStatExt", "Device (normally a client) sent a request to connect to another device and is waiting for confirmation (normally from a server)." );
    } else if( state == "SYN_RECV" ) {
        txt = qApp->translate("NetStatExt", "The device has received a connection request and send the acknowledgment. At the moment, is expected to start the connection." );
    } else if( state == "ESTABLISHED" ) {
        txt = qApp->translate("NetStatExt", "The \"steady state\" of an open TCP connection. Data can be exchanged freely once both devices in the connection enter this state. This will continue until the connection is closed." );
    }  else if( state == "CLOSE_WAIT" ) {
        txt = qApp->translate("NetStatExt", "One of the stages of closing the connection. Connection closing sent the request to the remote device." );
    }  else if( state == "LAST_ACK" ) {
        txt = qApp->translate("NetStatExt", "The last stage of closing the connection. Connection closing sent the request to the remote device." );
    }  else if( state == "FIN_WAIT1" ) {
        txt = qApp->translate("NetStatExt", "One of the stages of closing the connection. Connection closing sent the request to the local device." );
    }  else if( state == "FIN_WAIT2" ) {
        txt = qApp->translate("NetStatExt", "One of the stages of closing the connection. Connection closing sent the request to the local device." );
    }  else if( state == "CLOSING" ) {
        txt = qApp->translate("NetStatExt", "One of the stages of closing the connection. The simultaneous closure of the connection." );
    }  else if( state == "TIME_WAIT" ) {
        txt = qApp->translate("NetStatExt", "The last stage of closing the connection. After wait period, the connection pass to the CLOSED state." );
    }  else if( state == "CLOSE" ) {
        txt = qApp->translate("NetStatExt", "This is the state by default before the connection is established. This is the sitation when there is no any connection between devices : it eather hasn't been created yet, or has just been broken." );
    }

    return txt;
}


ParamAppN950 NetStatExt::findAppN950( const QString &path) const
{
    ParamAppN950 retVal;
    retVal.name = "";
    retVal.icon = "";

    if( path == "" ) {
        return retVal;
    }

    if( listDesktopFiles.length() == 0 ) {
        qDebug() << "NetStatExt::findAppN950(): Warning: len(listDesktopFiles) = 0 !!!";
        return retVal;
    }

    QFileInfo fileInf( path );
    /*
     *  basemane_path - содержит имя (без пути) запущенного файла, которое нашли в /proc
     *  т.е. имя запускаемого файла (путь убрали) */
    QString base_name = (fileInf.baseName()).trimmed();
    QList<QMap<QString, QString>*>::const_iterator itr;
    for( itr = listDesktopFiles.constBegin(); itr != listDesktopFiles.constEnd(); itr++ )
    {
        QMap<QString, QString>* pQMapData = *(itr);

        QString exec_proc = pQMapData->value("exec");
        QStringList exec_proc_l = exec_proc.split("%"); /* убираем опции, если есть */
        exec_proc = exec_proc_l.at( 0 );

        QString name_proc = pQMapData->value("name").mid(5); //с 5-ой позиции, т.к. впереди стоит слово Name=
        QString icon_proc = pQMapData->value("icon").mid(5); //с 5-ой позиции, т.к. впереди стоит слово Icon=

        int findme = exec_proc.indexOf( base_name, Qt::CaseInsensitive );
        if( findme != -1 )
        {
            //qDebug() << "***name_proc="<<name_proc<<" icon_proc="<<icon_proc;
            //qDebug() << "***exec_proc="<<exec_proc<<" base_name="<<base_name;
            retVal.name = name_proc;

            if( QFile::exists( icon_proc ) ) {
                retVal.icon = icon_proc;
                return retVal;
            }
            QString png(".png");
            QString svg(".svg");

            QString pi = QString("/usr/share/icons/hicolor/80x80/apps/") + icon_proc + png;
            //qDebug() << "***" << pi;
            if( QFile::exists( pi ) ) {
                retVal.icon = pi;
                return retVal;
            }

            pi = QString("/usr/share/icons/hicolor/scalable/apps/") + icon_proc + png;
            //qDebug() << "***" << pi;
            if( QFile::exists( pi ) ) {
                retVal.icon = pi;
                return retVal;
            }

            pi = QString("/usr/share/icons/hicolor/64x64/apps/") + icon_proc + png;
            //qDebug() << "***" << pi;
            if( QFile::exists( pi ) ) {
                retVal.icon = pi;
                return retVal;
            }

            pi = QString("/usr/share/icons/blanco/80x80/apps/") + icon_proc + png;
            //qDebug() << "***" << pi;
            if( QFile::exists( pi ) ) {
                retVal.icon = pi;
                return retVal;
            }

            pi = QString("/usr/share/icons/blanco/scalable/apps/") + icon_proc + png;
            //qDebug() << "***" << pi;
            if( QFile::exists( pi ) ) {
                retVal.icon = pi;
                return retVal;
            }

            pi = QString("/usr/share/themes/base/meegotouch/icons/") + icon_proc + svg;
            //qDebug() << "***" << pi;
            if( QFile::exists( pi ) ) {
                retVal.icon = pi;
                return retVal;
            }

            pi = QString("/usr/share/themes/base/meegotouch/icons/") + icon_proc + png;
            //qDebug() << "***" << pi;
            if( QFile::exists( pi ) ) {
                retVal.icon = pi;
                return retVal;
            }

            pi = QString("/usr/share/themes/blanco/meegotouch/icons/") + icon_proc + png;
            //qDebug() << "***" << pi;
            if( QFile::exists( pi ) ) {
                retVal.icon = pi;
                return retVal;
            }
        } //if( findme != -1 )
    } //for

    //qDebug() << "!!!" << retVal.name;
    //qDebug() << "!!!" << retVal.icon;

    return retVal;
}

/* По командной строке запуска приложения пробуем найти некоторые предопределенные приложения */
ParamAppN950 NetStatExt::predefineAppN9( const QString &path ) const
{
    ParamAppN950 retVal;
    retVal.name = "";
    retVal.icon = "";

    int findme = path.indexOf( "sshd", Qt::CaseInsensitive );
    if( findme != -1 ) {
        retVal.name = QString("SSH - ") + qApp->translate("PredefineAppN9", "server" );
        retVal.icon = "/usr/share/themes/blanco/meegotouch/icons/icon-l-settings.png";
        return retVal;
    }

    findme = path.indexOf( "ssh", Qt::CaseInsensitive );
    if( findme != -1 ) {
        retVal.name = "SSH - "+ qApp->translate("PredefineAppN9", "client" );
        retVal.icon = "/usr/share/themes/blanco/meegotouch/icons/icon-l-settings.png";
        return retVal;
    }

    findme = path.indexOf( "dnsmasq", Qt::CaseInsensitive );
    if( findme != -1 ) {
        retVal.name = QString("DNS - ")+ qApp->translate("PredefineAppN9", "daemon" );
        retVal.icon = "/usr/share/themes/blanco/meegotouch/icons/icon-l-settings.png";
        return retVal;
    }

    findme = path.indexOf( "QtWebProcess", Qt::CaseInsensitive );
    if( findme != -1 ) {
        retVal.name = "Web browser";
        retVal.icon = "/usr/share/themes/blanco/meegotouch/icons/icon-l-browser.png";
        return retVal;
    }

    findme = path.indexOf( "maps", Qt::CaseInsensitive );
    if( findme != -1 ) {
        retVal.name = "Maps";
        retVal.icon = "/usr/share/themes/blanco/meegotouch/icons/icon-l-maps.png";
        return retVal;
    }

    findme = path.indexOf( "fenix", Qt::CaseInsensitive );
    if( findme != -1 ) {
        retVal.name = "E-Mail";
        retVal.icon = "/usr/share/themes/blanco/meegotouch/icons/icon-l-mail.png";
        return retVal;
    }

    findme = path.indexOf( "messageserver", Qt::CaseInsensitive );
    if( findme != -1 ) {
        retVal.name = QString("E-Mail - ")+ qApp->translate("PredefineAppN9", "daemon" );
        retVal.icon = "/usr/share/themes/blanco/meegotouch/icons/icon-l-mail.png";
        return retVal;
    }

    findme = path.indexOf( "msyncd", Qt::CaseInsensitive );
    if( findme != -1 ) {
        retVal.name = QString("")+ qApp->translate("PredefineAppN9", "Synchronization backend" );
        retVal.icon = "/usr/share/themes/blanco/meegotouch/icons/icon-l-settings.png";
        return retVal;
    }

    return retVal;
}


void NetStatExt::clearListResult()
{
    if( listModel ) {
        listModel->clearList();
        delete listModel;
        listModel = NULL;
    }

    //очистка QList<QMap<QString, QVariant>*> data;
    QList<QMap<QString, QVariant>*>::const_iterator itr;
    for( itr = data.constBegin(); itr != data.constEnd(); itr++ )
    {
        QMap<QString, QVariant>* pQMapElement = *itr;
        delete pQMapElement;
    }
    this->data.clear();
}

void NetStatExt::clearListDesktop()
{
    //очистка QList<QMap<QString, QString>*> listDesktopFiles;
    QList<QMap<QString, QString>*>::const_iterator itr;
    for( itr = listDesktopFiles.constBegin(); itr != listDesktopFiles.constEnd(); itr++ )
    {
        QMap<QString, QString>* pQMapElement = *itr;
        delete pQMapElement;
    }
    this->listDesktopFiles.clear();
}
